﻿#include "Sistema.h"


Sistema::Sistema(int maxPaises, int maxCiudades, int maxRecursos)
{
	this->maxPais=maxPaises+(maxPaises/2);
	this->maxCiudades=maxCiudades+(maxCiudades/2);
	this->maxRecursos=maxRecursos+(maxRecursos/2);
	this->avlPaises=new AVLImp<Puntero<Pais>>();
	Puntero<FuncionHash<Cadena>> fh1=new CadenaFuncionHash(maxPaises);
	Puntero<FuncionIguales<Puntero<Pais>>> fi=new FuncionIgualesPais();
	Puntero<FuncionIguales<Cadena>> fIgualCadena=new FuncionIgualesCadena();
	Puntero<FuncionHash<Puntero<Pais>>> fh=new PaisFuncionHash(this->maxPais);
	this->heapPaises=new ColaHeapImp<Puntero<Pais>>(this->maxPais,fh,fi);
	this->hashPaises=new Hash<Cadena,Puntero<Pais>>(fh1,fIgualCadena,this->maxPais);
	Puntero<FuncionHash<Puntero<Recurso>>> fhRecur=new RecursoFuncionHash(this->maxRecursos);
	Puntero<FuncionIguales<Puntero<Recurso>>> fiRecur=new FuncionIgualesRecurso();
	this->hashRecOrdCron=new Hash<Puntero<Recurso>,ListaImp<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>>*>(fhRecur,fiRecur,this->maxRecursos);
	//fh1=NULL;
}

Sistema::~Sistema()
{
}


//// Tipo 1
TipoRetorno Sistema::AltaPais(Cadena nombrePais)
{

	Puntero<Pais> p = new Pais(nombrePais,this->maxCiudades); 
	if(this->hashPaises->size()<this->maxPais){
			if(!this->hashPaises->estaDefinido(nombrePais)){
				this->avlPaises->agregar(p);
				this->hashPaises->operator [](nombrePais) = p;	
				this->heapPaises->encolarConPrioridad(p,p->CantidadCiudades());
				return OK;
			}else{ 
				return ERROR;
			}
		}else{ 
				return ERROR;
			}
}

TipoRetorno Sistema::AltaCiudad(Cadena nombrePais, Cadena nombreCiudad, bool esCapital)
{
	Puntero<Ciudad> c=new Ciudad(nombreCiudad,esCapital,100);
	Puntero<Ciudad> c1=new Ciudad(nombreCiudad,esCapital,100);
if(this->hashPaises->operator [](nombrePais)->ObtenerHashCiudades()->size()<this->maxCiudades){	
	if(this->hashPaises->estaDefinido(nombrePais)){
				bool esta=this->hashPaises->operator [](nombrePais)->ObtenerCiudades()->esMiembro(c);
		if(!esta){
			this->hashPaises->operator [](nombrePais)->ObtenerCiudades()->agregar(c);
			if(esCapital)
			{
				if(this->hashPaises->operator [](nombrePais)->tieneCapital()==false){
					this->hashPaises->operator [](nombrePais)->SetCapital(c);
			
				}else{
					return ERROR;
				}
			}
			this->hashPaises->operator [](nombrePais)->ObtenerHashCiudades()->operator [](nombreCiudad)=c;
			Puntero<Pais> p=new Pais(nombrePais,this->maxCiudades);
			Puntero<Pais> p1=this->heapPaises->devolverDato(p);
			p1->sumarCiudad();
			this->heapPaises->encolarConPrioridad(p1,p1->CantidadCiudades());
			return OK;
		}else{
			return ERROR;
			}
		}else{
			return ERROR;
			}
	}else{
					return ERROR;
				}
}

TipoRetorno Sistema::AltaRecurso(Cadena nombrePais, Cadena nombreCiudad, Cadena recurso, int puntaje, int cantidad, int valor)
{
	Puntero<Pais> p = new Pais(nombrePais,this->maxCiudades); 
	Puntero<Recurso> r=new Recurso(recurso,puntaje,cantidad,valor);

		if(puntaje>=0&&cantidad>=0&&valor>=0){
				if(this->hashPaises->estaDefinido(nombrePais))
				{
					if(this->hashPaises->operator [](nombrePais)->ObtenerHashCiudades()->estaDefinido(nombreCiudad))
					{
						Puntero<Ciudad> c=this->hashPaises->operator [](nombrePais)->ObtenerHashCiudades()->operator [](nombreCiudad);
						Puntero<ColaHeapImp<Puntero<Recurso>>> recursos = c->ObtenerRecurso();
						if(c->ObtenerRecurso()->largo()<this->maxRecursos){
							if(recursos->EstaDefinido(r)){
							//ver si ya existe ese recurso asi lo atualizo
							Puntero<Recurso> r1=recursos->devolverDato(r);
							r->SumarCantidad(r1->ObtenerCantidad());
							recursos->encolarConPrioridad(r,puntaje);
							return OK;
						
						
							}else
							{
							ListaImp<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>> *l;
							recursos->encolarConPrioridad(r,r->ObtenerPuntaje());
							Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>> t= new Tupla<Puntero<Pais>,Puntero<Ciudad>>(p,c);

									if(!this->hashRecOrdCron->estaDefinido(r))
										{
											l = new ListaImp<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>>();
											//Para cada recurso nuevo ago un puntero a nuevo a lista
											l->AgregarFinal(t);
											this->hashRecOrdCron->operator [](r)=l;
										}else
												{
								
												l=this->hashRecOrdCron->operator [](r);
												l->AgregarFinal(t);
										}

										return OK;
							}
					}else{
						return ERROR;
					}
			}else{
			return ERROR;
			}
				
		}else{
			return ERROR;
		}
	}else{
		return ERROR;
	}
		//return NO_IMPLEMENTADA;

}

Tupla<TipoRetorno, Puntero<Iterador<pPais>>> Sistema::ImprimirJuego() const
{
	if(this->avlPaises->esVacio()==false){
	this->avlPaises->CrearIterador();//lleno lista iter
	Puntero<Iterador<Puntero<Pais>>> it= this->avlPaises->ObtenerIterador();
	Puntero<Iterador<pPais>> it2=  new CastIterador<Pais,IPais>(it);
	while(it->HayElemento()){
		Puntero<Pais> p = it->ElementoActual();
		cout<<"Pais: "<<p->ObtenerNombre()<<endl;
		p->ObtenerCiudades()->CrearIterador();
		Puntero<Iterador<Puntero<Ciudad>>> it2=p->ObtenerCiudades()->ObtenerIterador();
		while(it2->HayElemento())
		{
			cout<<"		 "<<it2->ElementoActual()->ObtenerNombre()<<endl;
			Puntero<Iterador<Puntero<Recurso>>> it3=it2->ElementoActual()->ObtenerRecurso()->ObtenerIterador();
			while(it3->HayElemento()){
				cout<<"				"<<it3->ElementoActual()->ObtenerNombre()<<endl;
				it3->Avanzar();
			}
			it2->Avanzar();
		}

		it->Avanzar();
	}
	return Tupla<TipoRetorno, Puntero<Iterador<pPais>>>(OK,it2);

	}else{
		return ERROR;
	}
	
}

Tupla<TipoRetorno, pRecurso> Sistema::RecursoMasValioso(Cadena nombrePais, Cadena nombreCiudad)
{
if(this->hashPaises->estaDefinido(nombrePais))
	{
		
		if(this->hashPaises->operator [](nombrePais)->ObtenerHashCiudades()->estaDefinido(nombreCiudad))
		{
						//validar si no existe la ciudad y si no existe el pais
					Puntero<Ciudad> c= this->hashPaises->operator [](nombrePais)->ObtenerHashCiudades()->operator [](nombreCiudad);
					if(c->ObtenerRecurso()->esVacia()==false){
							
							Puntero<Recurso> r=c->ObtenerRecurso()->DevolverMaximo();
							if(r!=NULL){
							//c->ObtenerRecurso()->desencolar(r);
								cout<<r->ObtenerNombre();
							Puntero<IRecurso> iR=r.Cast<IRecurso>();

							return Tupla<TipoRetorno, pRecurso>(OK, iR);
							}else{
								return Tupla<TipoRetorno, pRecurso>(OK, NULL);
							}
						}else{
									return ERROR;
						}
					
		}else{
		return ERROR;
		}
	}else{
		return ERROR;
	}
}


//// Tipo 2
TipoRetorno Sistema::BuscarRecursos(Puntero<Iterable<Cadena>> recursos) const
{
	Puntero<FuncionHash<Puntero<Recurso>>> fh=new RecursoFuncionHash(this->maxRecursos);
	Puntero<FuncionIguales<Puntero<Recurso>>> fi=new FuncionIgualesRecurso();
	Puntero<Iterador<Cadena>> itRecurso =recursos->ObtenerIterador();
	bool existenPaises=false;
	bool existenCuidades= false;
	bool imprimirCiudad=false;
	bool estaRecEnCiudad;
	double sumaTotalCuidad=0;
	double sumaTotalPais =0;
	double sumaTotal=0;
	if(this->hashPaises->size()>0){
	Puntero<Hash<Puntero<Recurso>,bool>> hashNoEncontrados=new Hash<Puntero<Recurso>,bool>(fh,fi,this->maxRecursos);
			Puntero<Recurso>  rec;
		this->avlPaises->CrearIterador();
		Puntero<Iterador<Puntero<Pais>>> paises= this->avlPaises->ObtenerIterador();
		
		while(itRecurso->HayElemento()){
			rec=new Recurso(itRecurso->ElementoActual(),0,0,0);
			hashNoEncontrados->operator [](rec)=false;
			itRecurso->Avanzar();
		}	
		while(paises->HayElemento())
		{
			existenPaises=true;
			Puntero<Pais>  pais=paises->ElementoActual();
			pais->ObtenerCiudades()->CrearIterador();
			cout<<pais->ObtenerNombre()<<endl;
			
			Puntero<Iterador<Puntero<Ciudad>>> ciudades= pais->ObtenerCiudades()->ObtenerIterador();
			while(ciudades->HayElemento())
			{
				existenCuidades=true;
				itRecurso->Reiniciar();
				while(itRecurso->HayElemento())
				{
					rec=new Recurso(itRecurso->ElementoActual(),0,0,0);
					
					if(ciudades->ElementoActual()->ObtenerRecurso()->EstaDefinido(rec))
					{
						Puntero<Recurso> recEncontrado = ciudades->ElementoActual()->ObtenerRecurso()->devolverDato(rec);
						hashNoEncontrados->operator [](recEncontrado)=true;
						if(imprimirCiudad==false){
							if(ciudades->ElementoActual()->esCapital()){
						cout<<"       *"<<ciudades->ElementoActual()->ObtenerNombre()<<endl;
								}else{
								cout<<"       "<<ciudades->ElementoActual()->ObtenerNombre()<<endl;
								}
						imprimirCiudad=true;
						}
						cout<<"                   "<<recEncontrado->ObtenerNombre()<<"-"<<recEncontrado->ObtenerPuntaje()<<endl;
						sumaTotalPais=sumaTotalPais+recEncontrado->ObtenerPuntaje();
						sumaTotalCuidad=sumaTotalCuidad+recEncontrado->ObtenerPuntaje();
						
						//Sumo el puntaje al pais
						estaRecEnCiudad=true;
					}
				itRecurso->Avanzar();
				}
				if(sumaTotalCuidad>0){
					cout<<"       "<<"Total: "<<sumaTotalCuidad<<endl;
				}
				sumaTotalCuidad=0;
				imprimirCiudad=false;
				ciudades->Avanzar();
			}

			
			cout<<"Total Pais: "<<sumaTotalPais<<endl;
			sumaTotal=sumaTotal+sumaTotalPais;
			sumaTotalPais=0;
			paises->Avanzar();
			
			
		}
		
	itRecurso->Reiniciar();
	cout<<"Total Global:"<<sumaTotal<<endl;
	bool esta;
	cout<<"Recursos que no estan en ninguna ciudad:"<<endl;
	while(itRecurso->HayElemento()){
			rec=new Recurso(itRecurso->ElementoActual(),0,0,0);
			esta=hashNoEncontrados->operator [](rec);
			if(esta==false){
			cout<<itRecurso->ElementoActual()<<endl;
			
			}
			itRecurso->Avanzar();
		}	
	if(existenCuidades||existenPaises ){
			return OK;
	}else{
			return ERROR;
	}
}else{
		return ERROR;
	}
}

TipoRetorno Sistema::BorrarPais(Cadena nombrePais)
{
	
	Puntero<Pais> p1=new Pais(nombrePais,this->maxCiudades);
	Puntero<Pais> p2;
	if(this->hashPaises->size()>0){
		if(this->hashPaises->estaDefinido(nombrePais))
		{		
			Puntero<Pais> p=this->hashPaises->operator [](nombrePais);
			this->avlPaises->borrarNodo(p);
			this->hashPaises->operator [](nombrePais)=p1;
			this->hashPaises->eliminar(nombrePais);
	//		p2=this->heapPaises->devolverDato(p);
	//		this->heapPaises->encolarConPrioridad(p2,0);



		//ver lo de la cola orden crono
			return OK;
		}else{
			return ERROR;
		}
	}else{
			return ERROR;
	}
}

TipoRetorno Sistema::BuscarRecMejorPuntaje(Puntero<Iterable<Cadena>> recursos) const
{
	Puntero<FuncionHash<Cadena>> fh=new CadenaFuncionHash(this->maxCiudades);
	Puntero<FuncionIguales<Cadena>> fi=new FuncionIgualesCadena();
	Puntero<Hash<Cadena,Puntero<ListaImp<Puntero<Tupla<Puntero<Ciudad>,Puntero<Recurso>>>>>>> hashRec=new Hash<Cadena,Puntero<ListaImp<Puntero<Tupla<Puntero<Ciudad>,Puntero<Recurso>>>>>>(fh,fi,this->maxRecursos);
	ListaImp<Puntero<Tupla<Puntero<Ciudad>,Puntero<Recurso>>>>* l;
	Puntero<Iterador<Cadena>> itRecurso =recursos->ObtenerIterador();
	bool existenPaises=false;
	bool existenCuidades= false;
	bool imprimio=false;
		
	if(this->avlPaises->esVacio()==false){
		this->avlPaises->CrearIterador();//creo el It
		Puntero<Iterador<Puntero<Pais>>> paises= this->avlPaises->ObtenerIterador();
		Puntero<Tupla<Puntero<Ciudad>,Puntero<Recurso>>> ini=new Tupla<Puntero<Ciudad>,Puntero<Recurso>>(new Ciudad("Sin nombre",false,this->maxRecursos),new Recurso("rec",0,0,0)); 

		while(paises->HayElemento())
		{
			existenPaises=true;
			Puntero<Pais>  pais=paises->ElementoActual();
			cout<<pais->ObtenerNombre()<<endl;
			pais->ObtenerCiudades()->CrearIterador();
			Puntero<Iterador<Puntero<Ciudad>>> ciudades= pais->ObtenerCiudades()->ObtenerIterador();
			
			while(ciudades->HayElemento())
			{
				l=new ListaImp<Puntero<Tupla<Puntero<Ciudad>,Puntero<Recurso>>>>();
				Puntero<Tupla<Puntero<Ciudad>,Puntero<Recurso>>> ini=new Tupla<Puntero<Ciudad>,Puntero<Recurso>>(new Ciudad("Sin nombre",false,this->maxRecursos),new Recurso("rec",0,0,0)); //new Tupla<Puntero<Ciudad>,Puntero<Recurso>>(NULL,NULL);
				l->Agregar(ini);// Inicializo
				hashRec->operator [](ciudades->ElementoActual()->ObtenerNombre())=l;
				
			
					while(itRecurso->HayElemento())
					{
						existenCuidades=true;
						Puntero<Recurso> rec=new Recurso(itRecurso->ElementoActual(),0,0,0);
						if(ciudades->ElementoActual()->ObtenerRecurso()->EstaDefinido(rec))
						{
							Puntero<Recurso> rAux=ciudades->ElementoActual()->ObtenerRecurso()->devolverDato(rec);
							Puntero<Tupla<Puntero<Ciudad>,Puntero<Recurso>>> t1=new Tupla<Puntero<Ciudad>,Puntero<Recurso>>(ciudades->ElementoActual(),rAux);
					
								if(l->ppio->dato->GetDato2()->ObtenerPuntaje() < rAux->ObtenerPuntaje()){
									l->Vaciar();
									l->Agregar(t1);
								}else{
									if(l->ppio->dato->GetDato2()->ObtenerPuntaje()==rAux->ObtenerPuntaje()){
										l->Agregar(t1);
									}	
								
								}	
						}
						itRecurso->Avanzar();

					}
				//cout<<pais->ObtenerNombre()<<endl;
			
				Puntero<ListaImp<Puntero<Tupla<Puntero<Ciudad>,Puntero<Recurso>>>>> li=hashRec->operator [](ciudades->ElementoActual()->ObtenerNombre());
				Puntero<Iterador<Puntero<Tupla<Puntero<Ciudad>,Puntero<Recurso>>>>> it=li->ObtenerIterador();
				while(it->HayElemento()){
					if(it->ElementoActual()->GetDato2()->ObtenerNombre()!="rec"){
								if(imprimio==false){
									if(ciudades->ElementoActual()->esCapital()){
										cout<<"	*"<< ciudades->ElementoActual()->ObtenerNombre()<<endl;
										}else{
										cout<<"	" <<ciudades->ElementoActual()->ObtenerNombre()<<endl;
										}
									imprimio=true;
								}
								cout<<"		"<< it->ElementoActual()->GetDato2()->ObtenerNombre()<<"-"<< it->ElementoActual()->GetDato2()->ObtenerPuntaje()<<endl;                     
								
					}			
					it->Avanzar();

				}
				imprimio=false;
				itRecurso->Reiniciar();
				ciudades->Avanzar();
			}
				
			paises->Avanzar();
		}

		return OK;
	}else{
		return ERROR;
	}
}
Tupla<TipoRetorno, Puntero<Iterador<Tupla<pPais, pCiudad>>>> Sistema::CiudadesRecurso(Cadena recurso) const
{
	
	ListaImp<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>> *l = new ListaImp<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>>();
	Puntero<Recurso> r=new Recurso(recurso,0,0,0);
	if(this->hashRecOrdCron->estaDefinido(r)){
		l=this->hashRecOrdCron->operator [](r);
		Puntero<Iterador<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>>> it=new IteradorListaImp<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>>(*l);
		ListaImp<Tupla<pPais,pCiudad>> *lista= new ListaImp<Tupla<pPais,pCiudad>> ();
		while(it->HayElemento())
		{
			Puntero<IPais> iP=it->ElementoActual()->GetDato1().Cast<IPais>();
			Puntero<ICiudad>iC=it->ElementoActual()->GetDato2().Cast<ICiudad>();
			Tupla<Puntero<IPais>,Puntero<ICiudad>> *t=new Tupla<Puntero<IPais>,Puntero<ICiudad>>(iP,iC);
			lista->Agregar(*t);
			it->Avanzar();
		}
		Puntero<Iterador<Tupla<pPais, pCiudad>>> it2= new IteradorListaImp<Tupla<pPais,pCiudad>>(*lista);

		 return Tupla<TipoRetorno, Puntero<Iterador<Tupla<pPais, pCiudad>>>>(OK,it2);
	}else{
		return ERROR;
	}
}

Tupla<TipoRetorno, pPais> Sistema::PaisMasCiudades() const
{
//retorno el maximo del heap paises
	if(!heapPaises->esVacia()){
		Puntero<Pais> p=this->heapPaises->DevolverMaximo();
		Puntero<IPais> iP=p.Cast<IPais>();
		return Tupla<TipoRetorno, pPais>(OK, iP);
		}else{
		return ERROR;
	}
}


//// Opcionales

TipoRetorno Sistema::Importar(Cadena nombrePais, Cadena nombreCiudad, Cadena nombreRecurso, int cantidad)
{
	Puntero<Recurso> rMenorPrecio=new Recurso("Sin Nombre",0,0,9999999);
	Puntero<Recurso> rPaisOrigen;
	Puntero<Pais> paisOrigen;
	Puntero<Ciudad> ciudadOrigen;
	ListaImp<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>> *l = new ListaImp<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>>();
	Puntero<Recurso> r=new Recurso(nombreRecurso,0,0,0);
	l=this->hashRecOrdCron->operator [](r);

	Puntero<Iterador<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>>> it=new IteradorListaImp<Puntero<Tupla<Puntero<Pais>,Puntero<Ciudad>>>>(*l);
	
	while(it->HayElemento()){
			Puntero<Recurso> rActual=it->ElementoActual()->GetDato2()->ObtenerRecurso()->devolverDato(r);
		if(rMenorPrecio!=NULL){
			if((rActual->ObtenerValor()<rMenorPrecio->ObtenerValor())&&(rActual->ObtenerCantidad()>=cantidad)){
				rMenorPrecio=it->ElementoActual()->GetDato2()->ObtenerRecurso()->devolverDato(r);
				ciudadOrigen=it->ElementoActual()->GetDato2();
				this->avlPaises->CrearIterador();
				Puntero<Iterador<Puntero<Pais>>> itPais= this->avlPaises->ObtenerIterador();
				while(itPais->HayElemento())
				{
					Puntero<Pais> p=itPais->ElementoActual();
					if(p->ObtenerHashCiudades()->estaDefinido(ciudadOrigen->ObtenerNombre()))
					{
						paisOrigen=p;
					}
					itPais->Avanzar();
				
				}

			}
		}
		it->Avanzar();
	}
	if(rMenorPrecio!=NULL){
		if(this->hashPaises->estaDefinido(nombrePais)){
		Puntero<Pais> p=this->hashPaises->operator [](nombrePais);
			if(p->ObtenerHashCiudades()->estaDefinido(nombreCiudad)){
				Puntero<Ciudad> c = p->ObtenerHashCiudades()->operator [](nombreCiudad);
				Puntero<ColaHeapImp<Puntero<Recurso>>> recursos = c->ObtenerRecurso();
				if(recursos->EstaDefinido(r)){
					//ver si ya existe ese recurso asi lo atualizo
					Puntero<Recurso> r1=recursos->devolverDato(r);
						if(r1->ObtenerValor()<rMenorPrecio->ObtenerValor()){
							r1->SetValor(rMenorPrecio->ObtenerValor());
						}
							r1->SetCantidad(r1->ObtenerCantidad()+cantidad);
							rMenorPrecio->SetCantidad(rMenorPrecio->ObtenerCantidad()-cantidad);
							cout<<"Se compro "<<cantidad <<"unidades de "<<rMenorPrecio->ObtenerNombre()<<" a "<<paisOrigen->ObtenerNombre()<<"-"<<ciudadOrigen->ObtenerNombre()<<endl;
							cout<<"Saldos "<<paisOrigen->ObtenerNombre()<<"-"<<ciudadOrigen->ObtenerNombre()<<":"<<rMenorPrecio->ObtenerCantidad()<<endl;
							cout<<"       "<<nombrePais<<"-"<<nombreCiudad<<":"<<cantidad<<endl;
					
							return OK;

						
				
					}else{
						AltaRecurso(nombrePais,nombreCiudad,rMenorPrecio->ObtenerNombre(),rMenorPrecio->ObtenerPuntaje(),cantidad,rMenorPrecio->ObtenerValor());
						rMenorPrecio->SetCantidad(rMenorPrecio->ObtenerCantidad()-cantidad);
						cout<<"Se compro "<<cantidad <<"unidades de "<<rMenorPrecio->ObtenerNombre()<<" a "<<paisOrigen->ObtenerNombre()<<"-"<<ciudadOrigen->ObtenerNombre()<<endl;
						cout<<"Saldos "<<paisOrigen->ObtenerNombre()<<"-"<<ciudadOrigen->ObtenerNombre()<<":"<<rMenorPrecio->ObtenerCantidad()<<endl;
						cout<<"       "<<nombrePais<<"-"<<nombreCiudad<<":"<<cantidad<<endl;
						
						return OK;
				}	
				}else{
					return ERROR;
				}
		}else{
			return ERROR;
		}
	}else{
	return ERROR;
	}

	
}
